package filesystem.server.file;

import java.util.LinkedList;
import java.util.concurrent.locks.ReentrantReadWriteLock;


/**
 * Classe que gerencia o acesso a um arquivo.
 *
 */
public class FileHandler{
	private String name;					///Nome pelo qual o arquivo será buscado
	private ReentrantReadWriteLock lock;	///Lock para proteção de leitura/escrita
	private LinkedList<FileHost> hosts;		///Hosts em que a versão está armazenada
	private	boolean deleted;				///Marcado para ser deletado (impede novas Leituras/Escritas)

	/**
	 * Cria o objeto responsável pelo gerenciamento do arquivo.
	 * 
	 * @param name Nome do arquivo no sistema.
	 * @param hash o hash do arquivo.
	 * @param path o caminho do arquivo no nó.
	 * @param host o nó em que está localizado o arquivo.
	 */
	protected FileHandler(String name, String path, String host){
		this.name  = name;
		this.lock  = new ReentrantReadWriteLock();
		this.hosts = new LinkedList<FileHost>();
		this.hosts.add(new FileHost(path, host));
		this.deleted = false;
	}

	/**
	 * Obtém o nome do arquivo.
	 * 
	 * @return Nome do arquivo.
	 */
	public String getName(){
		return new String(this.name);
	}
	
	/**
	 * Adiciona um host que contém o arquivo
	 * 
	 * @param path O caminho do arquivo no host
	 * @param host O host que contém o arquivo
	 */
	public void addHost(String path, String host){
		boolean add = true;
		for(FileHost fileHost: this.hosts){
			if(fileHost.getHost().equals(host)){
				add = false;
			}
		}
		if(add)
			this.hosts.add(new FileHost(path, host));
	}
	
	/**
	 * Obtém os hosts em que o arquivo está armazenado.
	 * 
	 * @return Lista com todos os hosts que contém o arquivo.
	 */
	public FileHost[] getHosts(){
		FileHost[] hosts = new FileHost[this.hosts.size()];
		for(int i = 0; i < hosts.length; i++){
			hosts[i] = this.hosts.get(i);
		}
		return hosts;
	}

	
	/**
	 * Requere uma operação de escrita no arquivo (não bloqueante).
	 * 
	 * @return true se a operação foi aceita, false, caso contrário.
	 */
	public boolean writeRequest(){
		if(!this.deleted){
			return this.lock.writeLock().tryLock();
		}
		return false;
	}

	/**
	 * Requere uma operação de escrita no arquivo (bloqueante).
	 */
	public void writeRequestBlock(){
		if(!this.deleted){
			this.lock.writeLock().lock();
		}
	}

	/**
	 * Finaliza a operação de escrita no arquivo.
	 * 
	 * @param hash O novo hash do arquivo.
	 * @param path O caminho no host do arquivo.
	 * @param host O host em que o arquivo modificado está armazenado.
	 */
	public void writeCommit(String path, String host){
		if(this.lock.writeLock().isHeldByCurrentThread()){
			this.hosts = new LinkedList<FileHost>();
			this.hosts.add(new FileHost(path, host));
			this.lock.writeLock().unlock();
		}
	}


	/**
	 * Requere uma operação de leitura no arquivo (não bloqueante).
	 * 
	 * @return true se a operação foi aceita, false, caso contrário.
	 */
	public boolean readRequest(){
		if(!this.deleted){
			return this.lock.readLock().tryLock();	
		}
		return false;
	}

	/**
	 * Requere uma operação de leitura no arquivo (bloqueante).
	 */
	public void readRequestBlock(){
		this.lock.readLock().lock();	
	}

	/**
	 * Finaliza a operação de leitura.
	 */
	public void readRelease(){
		if(this.lock.getReadHoldCount() >= 1){
			this.lock.readLock().unlock();
		}
	}
}
